package com.kexio.common.converter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 实体(Entity)与DTO通用转换工具
 * 支持单个对象转换、批量转换、自定义字段映射
 * 
 * @author Kexio
 */
public class EntityDTOConverter {
    
    private static final Logger logger = LoggerFactory.getLogger(EntityDTOConverter.class);
    
    /**
     * Entity转DTO（简单属性复制）
     * 
     * @param source 源对象（Entity）
     * @param targetClass 目标类型（DTO.class）
     * @return DTO对象
     */
    public static <S, T> T toDTO(S source, Class<T> targetClass) {
        if (source == null) {
            return null;
        }
        
        try {
            T target = targetClass.getDeclaredConstructor().newInstance();
            BeanUtils.copyProperties(source, target);
            return target;
        } catch (Exception e) {
            logger.error("Entity转DTO失败: source={}, target={}", 
                source.getClass().getName(), targetClass.getName(), e);
            throw new RuntimeException("对象转换失败", e);
        }
    }
    
    /**
     * Entity转DTO（带自定义字段处理）
     * 
     * @param source 源对象（Entity）
     * @param targetClass 目标类型（DTO.class）
     * @param customMapper 自定义字段映射函数
     * @return DTO对象
     */
    public static <S, T> T toDTO(S source, Class<T> targetClass, BiConsumer<S, T> customMapper) {
        if (source == null) {
            return null;
        }
        
        T target = toDTO(source, targetClass);
        if (customMapper != null && target != null) {
            customMapper.accept(source, target);
        }
        return target;
    }
    
    /**
     * DTO转Entity（简单属性复制）
     * 
     * @param source 源对象（DTO）
     * @param targetClass 目标类型（Entity.class）
     * @return Entity对象
     */
    public static <S, T> T toEntity(S source, Class<T> targetClass) {
        return toDTO(source, targetClass); // 底层逻辑相同
    }
    
    /**
     * DTO转Entity（带自定义字段处理）
     * 
     * @param source 源对象（DTO）
     * @param targetClass 目标类型（Entity.class）
     * @param customMapper 自定义字段映射函数
     * @return Entity对象
     */
    public static <S, T> T toEntity(S source, Class<T> targetClass, BiConsumer<S, T> customMapper) {
        return toDTO(source, targetClass, customMapper); // 底层逻辑相同
    }
    
    /**
     * DTO更新Entity（只更新非null字段）
     * 
     * @param source 源对象（DTO）
     * @param target 目标对象（Entity）
     */
    public static <S, T> void updateEntity(S source, T target) {
        if (source == null || target == null) {
            return;
        }
        
        BeanUtils.copyProperties(source, target, getNullPropertyNames(source));
    }
    
    /**
     * DTO更新Entity（带自定义字段处理）
     * 
     * @param source 源对象（DTO）
     * @param target 目标对象（Entity）
     * @param customMapper 自定义字段映射函数
     */
    public static <S, T> void updateEntity(S source, T target, BiConsumer<S, T> customMapper) {
        updateEntity(source, target);
        if (customMapper != null) {
            customMapper.accept(source, target);
        }
    }
    
    /**
     * 批量转换：Entity List → DTO List
     * 
     * @param sourceList 源列表（Entity List）
     * @param targetClass 目标类型（DTO.class）
     * @return DTO列表
     */
    public static <S, T> List<T> toDTOList(List<S> sourceList, Class<T> targetClass) {
        if (CollectionUtils.isEmpty(sourceList)) {
            return Collections.emptyList();
        }
        
        return sourceList.stream()
            .map(source -> toDTO(source, targetClass))
            .collect(Collectors.toList());
    }
    
    /**
     * 批量转换：Entity List → DTO List（带自定义映射）
     * 
     * @param sourceList 源列表（Entity List）
     * @param targetClass 目标类型（DTO.class）
     * @param customMapper 自定义字段映射函数
     * @return DTO列表
     */
    public static <S, T> List<T> toDTOList(List<S> sourceList, Class<T> targetClass, BiConsumer<S, T> customMapper) {
        if (CollectionUtils.isEmpty(sourceList)) {
            return Collections.emptyList();
        }
        
        return sourceList.stream()
            .map(source -> toDTO(source, targetClass, customMapper))
            .collect(Collectors.toList());
    }
    
    /**
     * 批量转换：DTO List → Entity List
     * 
     * @param sourceList 源列表（DTO List）
     * @param targetClass 目标类型（Entity.class）
     * @return Entity列表
     */
    public static <S, T> List<T> toEntityList(List<S> sourceList, Class<T> targetClass) {
        return toDTOList(sourceList, targetClass); // 底层逻辑相同
    }
    
    /**
     * 批量转换：DTO List → Entity List（带自定义映射）
     * 
     * @param sourceList 源列表（DTO List）
     * @param targetClass 目标类型（Entity.class）
     * @param customMapper 自定义字段映射函数
     * @return Entity列表
     */
    public static <S, T> List<T> toEntityList(List<S> sourceList, Class<T> targetClass, BiConsumer<S, T> customMapper) {
        return toDTOList(sourceList, targetClass, customMapper); // 底层逻辑相同
    }
    
    /**
     * 使用Function进行转换（更灵活）
     * 
     * @param source 源对象
     * @param converter 转换函数
     * @return 目标对象
     */
    public static <S, T> T convert(S source, Function<S, T> converter) {
        if (source == null || converter == null) {
            return null;
        }
        return converter.apply(source);
    }
    
    /**
     * 批量转换（使用Function）
     * 
     * @param sourceList 源列表
     * @param converter 转换函数
     * @return 目标列表
     */
    public static <S, T> List<T> convertList(List<S> sourceList, Function<S, T> converter) {
        if (CollectionUtils.isEmpty(sourceList) || converter == null) {
            return Collections.emptyList();
        }
        
        return sourceList.stream()
            .map(converter)
            .collect(Collectors.toList());
    }
    
    /**
     * 获取对象中值为null的属性名称数组
     */
    private static String[] getNullPropertyNames(Object source) {
        final java.beans.BeanInfo beanInfo;
        try {
            beanInfo = java.beans.Introspector.getBeanInfo(source.getClass());
        } catch (java.beans.IntrospectionException e) {
            return new String[0];
        }
        
        final java.beans.PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        final List<String> nullProperties = new ArrayList<>();
        
        for (java.beans.PropertyDescriptor pd : propertyDescriptors) {
            try {
                Object value = pd.getReadMethod().invoke(source);
                if (value == null) {
                    nullProperties.add(pd.getName());
                }
            } catch (Exception e) {
                // 忽略无法读取的属性
            }
        }
        
        return nullProperties.toArray(new String[0]);
    }
}

